﻿#include "HikCamera.h"

static int HikCameraObj = 0;
std::vector<std::pair<unsigned int, HikCamera *> > HikCameraPtr;
std::vector<std::pair<long, HikCamera *> > HikANPRCameraPtr;

void CALLBACK HikRealDataCallback(LONG playHandle, DWORD dataType, BYTE *buffer, DWORD bufferSize, void *context);
#ifdef _WIN32
void CALLBACK HikDecodeCallback(long port, char * buffer, long size, FRAME_INFO * frameInfo, long reserved1, long reserved2);
#elif __linux__
void CALLBACK HikDecodeCallback(int port, char * buffer, int size, FRAME_INFO * frameInfo, void* reserved1, int reserved2);
#endif

void CALLBACK HikMessageCallback(LONG command, NET_DVR_ALARMER *alarmer, char *alarmInfo, DWORD bufferLen, void* context);

HikCamera::HikCamera()
{
    type = HIK_CAMERA;
    windowId = 0;
    IPAddress = "192.168.1.64";
    port = 8000;
    userName = "admin";
    password = "password";
    channel = 1;
    stream = 0;
    callback = 0;
    context = 0;

    userID = -1;

    if (HikCameraObj == 0)
        HikCamera::InitSDK();

    HikCameraObj++;
}

HikCamera::~HikCamera()
{
    HikCameraObj--;

    if (HikCameraObj == 0)
        HikCamera::UninitSDK();
}

bool HikCamera::InitSDK()
{
    bool ret = false;

    ret = NET_DVR_Init();

    NET_DVR_SetConnectTime(2000, 1);
    NET_DVR_SetReconnect(10000, true);

    return ret;
}

bool HikCamera::UninitSDK()
{
    return NET_DVR_Cleanup();
}

#ifdef _WIN32
int HikCamera::Init(HWND windowId, const char *IPAddress, int port, const char *userName, const char *password, int channel, int stream)
#elif __linux__
int HikCamera::Init(unsigned int windowId, const char *IPAddress, int port, const char *userName, const char *password, int channel, int stream)
#endif
{
    this->windowId = windowId;
    this->IPAddress = IPAddress;
    this->port = port;
    this->userName = userName;
    this->password = password;
    this->channel = channel;
    this->stream = stream;

    return this->handle;
}

void HikCamera::SetCallback(VideoDataCallback callback, void *context)
{
    this->callback = callback;
    this->context = context;
}

void HikCamera::SetANPRInfoCallback(ANPRInfoCallback callback, void *context)
{
    anprInfoCallback = callback;
    anprInfoCallbackContext = context;
    anprMode = true;
}

int HikCamera::Connect()
{
    NET_DVR_DEVICEINFO_V30 struDeviceInfo;
    userID = NET_DVR_Login_V30((char *)IPAddress.c_str(), port, (char *)userName.c_str(), (char *)password.c_str(), &struDeviceInfo);
    if (userID < 0)
    {
        return (int)NET_DVR_GetLastError();
    }

    NET_DVR_PREVIEWINFO struPlayInfo = {0};
    struPlayInfo.hPlayWnd = (HWND)this->windowId;
    struPlayInfo.lChannel = channel;
    struPlayInfo.dwStreamType = stream;
    struPlayInfo.dwLinkMode = 0;
    struPlayInfo.bBlocked = 1;

    if (callback != 0)
        realPlayHandle = NET_DVR_RealPlay_V40(userID, &struPlayInfo, HikRealDataCallback, this);
    else
        realPlayHandle = NET_DVR_RealPlay_V40(userID, &struPlayInfo, 0, this);

    if (realPlayHandle < 0)
    {
        int ret = NET_DVR_GetLastError();
        NET_DVR_Logout(userID);
        return -1;
    }

    if (anprMode)
    {
        NET_DVR_SetDVRMessageCallBack_V30(HikMessageCallback, 0);
        NET_DVR_SETUPALARM_PARAM setupParam = { 0 };
        setupParam.dwSize = sizeof(NET_DVR_SETUPALARM_PARAM);

        setupParam.byAlarmInfoType = 1;

        // 布防优先级
        // 0: 一等级 (高), 1: 二等级 (中), 2: 三等级 (低)
        setupParam.byLevel = 1;

        alarmHandle = NET_DVR_SetupAlarmChan_V41(userID, &setupParam);
        if (alarmHandle < 0)
        {
            NET_DVR_Logout(userID);
            return NET_DVR_GetLastError();
        }

        HikANPRCameraPtr.push_back(std::pair<long, HikCamera *>(this->userID, this));
    }

    return 0;
}

int HikCamera::Disconnect()
{
    if (anprMode)
        NET_DVR_CloseAlarmChan_V30(alarmHandle);

    return NET_DVR_StopRealPlay(realPlayHandle);
}

int HikCamera::StartSaveVideo(const char *fileName)
{
    int ret = 0;

    if (!NET_DVR_SaveRealData(realPlayHandle, (char *)fileName))
    {
        ret = NET_DVR_GetLastError();

        std::cout << "NET_DVR_SaveRealData() error: " << ret << std::endl;
    }

    return ret;
}

int HikCamera::StopSaveVideo()
{
    int ret = 0;

    if (!NET_DVR_StopSaveRealData(realPlayHandle))
    {
        ret = NET_DVR_GetLastError();

        std::cout << "NET_DVR_StopSaveRealData() error: " << ret << std::endl;
    }

    return ret;
}

int HikCamera::GetTemperature(int ruleID, TEMPERATURE_INFO *temperature_info)
{
    DWORD ret = 0;

    NET_DVR_THERMOMETRYRULE_TEMPERATURE_INFO temperatureInfo;
    memset(&temperatureInfo, 0, sizeof(temperatureInfo));

    if (!NET_DVR_GetDVRConfig(userID, NET_DVR_GET_THERMOMETRYRULE_TEMPERATURE_INFO, ruleID, &temperatureInfo, sizeof(temperatureInfo), &ret))
    {
        std::cout << "HikCamera::GetTemperature(): NET_DVR_GetDVRConfig() error when getting NET_DVR_GET_THERMOMETRYRULE_TEMPERATURE_INFO" << std::endl;

        return ret;
    }

    temperature_info->max = temperatureInfo.fMaxTemperature;
    temperature_info->min = temperatureInfo.fMinTemperature;
    temperature_info->average = temperatureInfo.fAverageTemperature;

    return ret;
}

void CALLBACK HikRealDataCallback(LONG playHandle, DWORD dataType, BYTE *buffer, DWORD bufferSize, void *context)
{
    DWORD ret;
    LONG port;

    switch (dataType)
    {
    case NET_DVR_SYSHEAD:

        if (!PlayM4_GetPort(&port))
        {
            break;
        }

        if (!PlayM4_OpenStream(port, buffer, bufferSize, 1024 * 1024))
        {
            ret = PlayM4_GetLastError(port);
            break;
        }

        if (HikCameraPtr.size() == 0)
            HikCameraPtr.push_back(std::pair<unsigned int, HikCamera *>(port, (HikCamera *)context));
        else
        {
            bool exist = false;
            for (size_t i = 0; i < HikCameraPtr.size(); i++)
            {
                if (HikCameraPtr[i].first == port)
                {
                    exist = true;
                    break;
                }
            }

            if (!exist)
                HikCameraPtr.push_back(std::pair<unsigned int, HikCamera *>(port, (HikCamera *)context));
        }

        if (!PlayM4_SetDecCallBack(port, HikDecodeCallback))
        {
            ret = PlayM4_GetLastError(port);
            break;
        }

        if (!PlayM4_Play(port, 0))
        {
            ret = PlayM4_GetLastError(port);
            break;
        }

        if (!PlayM4_PlaySound(port))
        {
            ret = PlayM4_GetLastError(port);
            break;
        }

        break;
        
    case NET_DVR_STREAMDATA:

        for (int i = HikCameraPtr.size() - 1; i >= 0; i--)
        {
            if (HikCameraPtr[i].second == (HikCamera *)context)
            {
                port = HikCameraPtr[i].first;
                break;
            }
        }

        BOOL inData = PlayM4_InputData(port, buffer, bufferSize);

        while (!inData)
        {
#ifdef _WIN32
            Sleep(10);
#endif
            inData = PlayM4_InputData(port, buffer, bufferSize);
        }

        break;
    }
}

#ifdef _WIN32
void CALLBACK HikDecodeCallback(long port, char * buffer, long bufferSize, FRAME_INFO* frameInfo, long reserved1, long reserved2)
#elif __linux__
void CALLBACK HikDecodeCallback(int port, char * buffer, int bufferSize, FRAME_INFO* frameInfo, void* reserved1, int reserved2)
#endif
{
     long frameType = frameInfo->nType;    

    if (frameType == T_YV12)
    {
        for (int i = HikCameraPtr.size() - 1; i >= 0; i--)
        {
            if (HikCameraPtr[i].first == port)
            {
                HikCamera *camera = HikCameraPtr[i].second;

                if (camera->callback)
                    camera->callback(camera->handle, (unsigned char *)buffer, bufferSize, frameInfo->nWidth, frameInfo->nHeight, 2, camera->context);

                break;
            }
        }
    }
}

void CALLBACK HikMessageCallback(LONG command, NET_DVR_ALARMER *alarmer, char *alarmInfo, DWORD bufferLen, void* context)
{
    if (command == COMM_ITS_PLATE_RESULT)
    {
        NET_ITS_PLATE_RESULT plateResult = { 0 };
        memcpy(&plateResult, alarmInfo, sizeof(plateResult));

        std::string plateNumber = plateResult.struPlateInfo.sLicense;

        if ((plateResult.struPicInfo[0].dwDataLen != 0) && (plateResult.struPicInfo[0].byType == 1) || (plateResult.struPicInfo[0].byType == 2))
        {
            cv::Mat image;
            image = cv::imdecode(cv::Mat(1, plateResult.struPicInfo[0].dwDataLen, CV_8UC1, plateResult.struPicInfo[0].pBuffer), cv::IMREAD_UNCHANGED);

            for (int i = 0; i < HikANPRCameraPtr.size(); i++)
            {
                if (HikANPRCameraPtr[i].first == alarmer->lUserID)
                {
                    HikCamera *camera = HikANPRCameraPtr[i].second;

                    if (camera->anprInfoCallback)
                        camera->anprInfoCallback(camera->handle, plateNumber.c_str(), (unsigned char*)image.data, image.step * image.rows, image.cols, image.rows, camera->anprInfoCallbackContext);
                }
            }
        }
    }
}
